#include "CHanoi.h"

CHanoi::CHanoi()
{
    //ctor
    m_nLayers = MAX_LAYERS;
    m_lpiHeight[0] = m_nLayers;
    m_lpiHeight[1] = 0;
    m_lpiHeight[2] = 0;
    Init();
}

CHanoi::~CHanoi()
{
}

void CHanoi::Init()
{
    int i;
    for(i = 0; i < m_nLayers; ++i)
    {
        m_lpiColumn[i] = m_nLayers - i - 1;
    }
    m_lpiHeight[0] = m_nLayers;
    m_lpiHeight[1] = 0;
    m_lpiHeight[2] = 0;
    m_lpiSteps[0] = 0;
    m_lpiSteps[1] = 0;
}


int CHanoi::TestMove(int iSrc, int iDest)
{
    int iSelect, iResult;
    iSelect = m_lpiColumn[iSrc * MAX_LAYERS + m_lpiHeight[iSrc] - 1];
    if
    (
        m_lpiSteps[1] > 0 && m_lpiSteps[0] < m_lpiSteps[1] &&
        iSrc == m_lpiRecords[m_lpiSteps[0] * 2] &&
        iDest == m_lpiRecords[m_lpiSteps[0] * 2 + 1]
    )
    {
        iResult = 0;
    }
    else if
    (
        m_lpiHeight[iDest] == 0 ||
        iSelect < m_lpiColumn[iDest * MAX_LAYERS + m_lpiHeight[iDest] - 1]
    )
    {
        iResult = 1;
    }
    else
    {
        iResult = -1;
    }
    return iResult;
}

int CHanoi::BaseMove(int iSrc, int iDest)
{
    int iSelect, iSucceed;
    iSelect = m_lpiColumn[iSrc * MAX_LAYERS + m_lpiHeight[iSrc] - 1];
    if (TestMove(iSrc, iDest) != -1)
    {
        m_lpiColumn[iDest * MAX_LAYERS + m_lpiHeight[iDest]] = iSelect;
        m_lpiHeight[iSrc] -= 1;
        m_lpiHeight[iDest] += 1;
        iSucceed = 1;
    }
    else
    {
        iSucceed = 0;
    }
    return iSucceed;
}

int CHanoi::MoveWithRecord(int iSrc, int iDest)
{
    int iSucceed;
    iSucceed = BaseMove(iSrc, iDest);
    if (iSucceed == 1)
    {
        m_lpiRecords[m_lpiSteps[0] * 2] = iSrc;
        m_lpiRecords[m_lpiSteps[0] * 2 + 1] = iDest;
        m_lpiSteps[0] += 1;
        m_lpiSteps[1] = m_lpiSteps[0];
    }
    return iSucceed;
}

int CHanoi::Forward()
{
    int result;
    if (m_lpiSteps[1] > 0 && m_lpiSteps[0] < m_lpiSteps[1])
    {
        BaseMove(m_lpiRecords[m_lpiSteps[0] * 2], m_lpiRecords[m_lpiSteps[0] * 2 + 1]);
        m_lpiSteps[0] += 1;
        result = m_lpiRecords[m_lpiSteps[0] * 2 - 1];
    }
    else
    {
        result = -1;
    }
    return result;
}

int CHanoi::Backward()
{
    int result;
    if (m_lpiSteps[0] > 0)
    {
        BaseMove(m_lpiRecords[m_lpiSteps[0] * 2 - 1], m_lpiRecords[m_lpiSteps[0] * 2 - 2]);
        m_lpiSteps[0] -= 1;
        result = m_lpiRecords[m_lpiSteps[0] * 2];
    }
    else
    {
        result = -1;
    }
    return result;
}


void CHanoi::SetLayers(int iNewLayers)
{
    if (iNewLayers < 1)
    {
        iNewLayers = 1;
    }
    if (iNewLayers > MAX_LAYERS)
    {
        iNewLayers = MAX_LAYERS;
    }
    m_nLayers = iNewLayers;
    Init();
}

void CHanoi::GenerateAuto()
{
    int lpiStackSrc[256], lpiStackDest[256], lpiStackObstacle[256], nStackLen, iSrc, iDest, iObstacle, iBuffer;
    m_lpiSteps[0] = 0;
    m_lpiSteps[1] = 0;
    lpiStackSrc[0] = 0;
    lpiStackDest[0] = 2;
    lpiStackObstacle[0] = m_nLayers - 1;
    nStackLen = 1;
    while (nStackLen > 0)
    {
        --nStackLen;
        iSrc = lpiStackSrc[nStackLen];
        iDest = lpiStackDest[nStackLen];
        iObstacle = lpiStackObstacle[nStackLen];
        if (iObstacle == 0)
        {
            m_lpiRecords[m_lpiSteps[1] * 2] = iSrc;
            m_lpiRecords[m_lpiSteps[1] * 2 + 1] = iDest;
            m_lpiSteps[1] += 1;
        }
        else
        {
            if (nStackLen > 253)
            {
                break;
            }
            --iObstacle;
            iBuffer = 3 - iSrc - iDest;
            lpiStackSrc[nStackLen] = iBuffer;
            lpiStackDest[nStackLen] = iDest;
            lpiStackObstacle[nStackLen] = iObstacle;
            ++nStackLen;
            lpiStackSrc[nStackLen] = iSrc;
            lpiStackDest[nStackLen] = iDest;
            lpiStackObstacle[nStackLen] = 0;
            ++nStackLen;
            lpiStackSrc[nStackLen] = iSrc;
            lpiStackDest[nStackLen] = iBuffer;
            lpiStackObstacle[nStackLen] = iObstacle;
            ++nStackLen;
        }
    }
}
